//! Rust integration layer for Zig syscall implementation
//! SPDX-License-Identifier: MPL-2.0

use core::ffi::{c_char, c_void};
use crate::prelude::*;
use crate::process::Process;
use crate::thread::Thread;

/// C bindings to Zig syscall functions
extern "C" {
    fn c_handle_syscall(
        syscall_num: u64,
        arg0: u64,
        arg1: u64,
        arg2: u64,
        arg3: u64,
        arg4: u64,
        arg5: u64,
        context: *mut c_void,
    ) -> u64;
    
    fn c_get_syscall_name(num: u64) -> *const c_char;
    
    fn c_get_syscall_stats() -> SyscallStats;
}

/// Syscall statistics structure (must match C definition)
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct SyscallStats {
    pub calls: u64,
    pub total_time: u64,
    pub errors: u64,
}

/// Context structure passed to Zig syscall handlers
#[repr(C)]
pub struct ZigSyscallContext {
    pub current_thread: *mut Thread,
    pub current_process: *mut Process,
    pub user_space_ptr: *mut c_void,
}

impl ZigSyscallContext {
    /// Create a new context from current thread and process
    pub fn new(thread: &mut Thread, process: &mut Process) -> Self {
        Self {
            current_thread: thread as *mut Thread,
            current_process: process as *mut Process,
            user_space_ptr: core::ptr::null_mut(), // TODO: Set actual user space pointer
        }
    }
}

/// Zig syscall handler wrapper
pub struct ZigSyscallHandler;

impl ZigSyscallHandler {
    /// Handle a system call using the Zig implementation
    pub fn handle_syscall(
        syscall_num: u64,
        args: [u64; 6],
        thread: &mut Thread,
        process: &mut Process,
    ) -> Result<u64> {
        let mut context = ZigSyscallContext::new(thread, process);
        
        let result = unsafe {
            c_handle_syscall(
                syscall_num,
                args[0],
                args[1],
                args[2],
                args[3],
                args[4],
                args[5],
                &mut context as *mut _ as *mut c_void,
            )
        };
        
        // Convert result to Rust Result type
        // In Linux, negative values indicate errors
        if (result as i64) < 0 {
            let errno = (-(result as i64)) as u32;
            Err(Error::with_message(
                Errno::from(errno),
                &format!("Zig syscall {} failed", syscall_num),
            ))
        } else {
            Ok(result)
        }
    }
    
    /// Get syscall name for debugging
    pub fn get_syscall_name(num: u64) -> Option<&'static str> {
        let name_ptr = unsafe { c_get_syscall_name(num) };
        if name_ptr.is_null() {
            None
        } else {
            unsafe {
                let cstr = core::ffi::CStr::from_ptr(name_ptr);
                cstr.to_str().ok()
            }
        }
    }
    
    /// Get syscall statistics
    pub fn get_stats() -> SyscallStats {
        unsafe { c_get_syscall_stats() }
    }
}

/// Integration with existing Asterinas syscall infrastructure
pub mod integration {
    use super::*;
    use crate::syscall::{SyscallHandler, SyscallResult};
    
    /// Zig-based syscall handler that implements the Asterinas SyscallHandler trait
    pub struct ZigSyscallHandlerImpl;
    
    impl SyscallHandler for ZigSyscallHandlerImpl {
        fn handle(
            &self,
            syscall_num: u64,
            args: [u64; 6],
            current: &CurrentTask,
        ) -> SyscallResult {
            // Extract thread and process from CurrentTask
            let thread = current.thread();
            let process = current.process();
            
            // Call Zig implementation
            match ZigSyscallHandler::handle_syscall(
                syscall_num,
                args,
                thread,
                process,
            ) {
                Ok(value) => SyscallResult::Return(value as isize),
                Err(err) => SyscallResult::Err(err),
            }
        }
        
        fn name(&self) -> &'static str {
            "ZigSyscallHandler"
        }
    }
    
    /// Register the Zig syscall handler for specific syscalls
    pub fn register_zig_handlers() -> Result<()> {
        let handler = ZigSyscallHandlerImpl;
        
        // Register for specific syscalls that are implemented in Zig
        // This allows gradual migration from Rust to Zig
        
        // File system syscalls
        register_syscall_handler(0, Box::new(handler))?; // read
        register_syscall_handler(1, Box::new(handler))?; // write
        register_syscall_handler(2, Box::new(handler))?; // open
        register_syscall_handler(3, Box::new(handler))?; // close
        
        // Memory management syscalls
        register_syscall_handler(9, Box::new(handler))?;  // mmap
        register_syscall_handler(10, Box::new(handler))?; // mprotect
        register_syscall_handler(11, Box::new(handler))?; // munmap
        register_syscall_handler(12, Box::new(handler))?; // brk
        
        // Process management syscalls
        register_syscall_handler(39, Box::new(handler))?; // getpid
        register_syscall_handler(57, Box::new(handler))?; // fork
        register_syscall_handler(59, Box::new(handler))?; // execve
        register_syscall_handler(60, Box::new(handler))?; // exit
        
        // Time syscalls
        register_syscall_handler(96, Box::new(handler))?;  // gettimeofday
        register_syscall_handler(201, Box::new(handler))?; // time
        register_syscall_handler(35, Box::new(handler))?;  // nanosleep
        
        Ok(())
    }
    
    // Placeholder for syscall registration function
    // This would integrate with the actual Asterinas syscall registration system
    fn register_syscall_handler(
        _num: u64,
        _handler: Box<dyn SyscallHandler>,
    ) -> Result<()> {
        // TODO: Implement actual registration with Asterinas syscall dispatcher
        Ok(())
    }
}

/// Performance monitoring for Zig syscalls
pub mod perf {
    use super::*;
    use core::sync::atomic::{AtomicU64, Ordering};
    
    static ZIG_SYSCALL_COUNT: AtomicU64 = AtomicU64::new(0);
    static ZIG_SYSCALL_TIME: AtomicU64 = AtomicU64::new(0);
    static ZIG_SYSCALL_ERRORS: AtomicU64 = AtomicU64::new(0);
    
    /// Record a syscall execution
    pub fn record_syscall(duration_ns: u64, success: bool) {
        ZIG_SYSCALL_COUNT.fetch_add(1, Ordering::Relaxed);
        ZIG_SYSCALL_TIME.fetch_add(duration_ns, Ordering::Relaxed);
        
        if !success {
            ZIG_SYSCALL_ERRORS.fetch_add(1, Ordering::Relaxed);
        }
    }
    
    /// Get performance statistics
    pub fn get_perf_stats() -> (u64, u64, u64) {
        (
            ZIG_SYSCALL_COUNT.load(Ordering::Relaxed),
            ZIG_SYSCALL_TIME.load(Ordering::Relaxed),
            ZIG_SYSCALL_ERRORS.load(Ordering::Relaxed),
        )
    }
    
    /// Reset performance counters
    pub fn reset_stats() {
        ZIG_SYSCALL_COUNT.store(0, Ordering::Relaxed);
        ZIG_SYSCALL_TIME.store(0, Ordering::Relaxed);
        ZIG_SYSCALL_ERRORS.store(0, Ordering::Relaxed);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_zig_syscall_context_creation() {
        // This would require actual Thread and Process instances
        // TODO: Implement when integration is complete
    }
    
    #[test]
    fn test_syscall_name_lookup() {
        // Test known syscall numbers
        assert!(ZigSyscallHandler::get_syscall_name(0).is_some()); // read
        assert!(ZigSyscallHandler::get_syscall_name(1).is_some()); // write
        assert!(ZigSyscallHandler::get_syscall_name(999999).is_none()); // invalid
    }
    
    #[test]
    fn test_performance_monitoring() {
        perf::reset_stats();
        
        perf::record_syscall(1000, true);
        perf::record_syscall(2000, false);
        
        let (calls, time, errors) = perf::get_perf_stats();
        assert_eq!(calls, 2);
        assert_eq!(time, 3000);
        assert_eq!(errors, 1);
    }
}